# 读写一个二进制数组的结构化数据到Python 元组中

################################################################################
# 结构体通常会使用一些结构码值 i, d, f 等[参考 Python 文档]。
# 这些代码分别代表某个特定的二进制数据类型如 32 位整数，64 位浮点数，32 位浮点数等。
# 第一个字符 < 指定了字节顺序。在这个例子中，它表示 ”低位在前”。更改这个字符为 > 表示高位在前，
# 或者是 ! 表示网络字节顺序。
################################################################################

from struct import Struct

def write_records(records, format, f):
    record_struct = Struct(format)
    for r in records:
        f.write(record_struct.pack(*r))

def read_records(format, f):
    record_struct = Struct(format)
    chunks = iter(lambda: f.read(record_struct.size), b'')
    return (record_struct.unpack(chunk) for chunk in chunks)

def unpack_records(format, data):
    ############################################################################
    # unpack_from() 对于从一个大型二进制数组中提取二进制数据非常有用，因为它不会产生任何的
    # 临时对象或者进行内存复制操作。你只需要给它一个字节字符串(或数组) 和一个字节偏移量，
    # 它会从那个位置开始直接解包数据。
    ############################################################################

    record_struct = Struct(format)
    return (record_struct.unpack_from(data, offset) for offset in range(0, len(data), record_struct.size))

if __name__ == '__main__':
    records = [(1, 2.3, 4.5), (6, 7.8, 9.0), (12, 13.4, 56.7)]
    with open('data.b', 'wb') as f:
        # Little endian 32-bit integer, two double precision floats
        write_records(records, '<idd', f)

    with open('data.b', 'rb') as f:
        for record in read_records('<idd', f):
            print(record)

    print('\n=================================\n')

    with open('data.b', 'rb') as f:
        data = f.read()
        for rec in unpack_records('<idd', data):
            print(rec)
